package timetabling;

import generalEvolutionaryAlgorithm.EABlackBoxFunction;
import generalEvolutionaryAlgorithm.EAInstance;
import generalEvolutionaryAlgorithm.EAResult;

import java.util.HashSet;

public class TimetablingBlackBoxFunction extends EABlackBoxFunction {
	private boolean[][] studentAttendsEventMatrix;
	private boolean[][] eventCanTakePartInRoom;
	private HashSet<Integer>[] studentAttendsEvent;
	private int students, events;
	
	@SuppressWarnings("unchecked")
	public TimetablingBlackBoxFunction(boolean[][] studentAttendsEvent, boolean[][] eventCanTakePartInRoom) {
		this.studentAttendsEventMatrix = studentAttendsEvent;
		this.eventCanTakePartInRoom = eventCanTakePartInRoom;
		students = this.studentAttendsEventMatrix.length;
		events = this.studentAttendsEventMatrix[0].length;
		this.studentAttendsEvent = new HashSet[students];
		for(int student = 0; student < students; ++student){
			this.studentAttendsEvent[student] = new HashSet<Integer>();
			for(int event = 0; event < events; ++event){
				if(studentAttendsEventMatrix[student][event]){
					this.studentAttendsEvent[student].add(event);
				}
			}
		}
	}

	@Override
	protected EAResult specificEvaluation(EAInstance eaInstance) {
		TimetablingInstance instance = (TimetablingInstance) eaInstance;
		int[] timeForEvent = instance.getTimeForEvent();
		int[] roomForEvent = instance.getRoomForEvent();
		// long result = 0;
		int eventTakesPlaceInRoomWithoutNeededFeatures = 0;
		int numberStudentsHaveMoreLecturesAtSameTime = 0;
		int longConsecutiveLectures = 0;
		int unscheduledEvents = 0;
		int onlyOneLectureADay = 0;
		int lastTimeSlotADay = 0;
		int days = TimetablingConstants.DAYS;
		int hours = TimetablingConstants.HOURS;
		int spots = days * hours;
		for(int event = 0; event < events; event++){
			if(roomForEvent[event] != -1){
				if(!eventCanTakePartInRoom[event][roomForEvent[event]]){
					++eventTakesPlaceInRoomWithoutNeededFeatures;
				}
			}
		}
		for(int event = 0; event < events; event++){
			if(timeForEvent[event] == -1){
				unscheduledEvents++;
			}
		}
		for(int student = 0; student < students; student++){
			int[] countLectures = new int[spots];
			for(int event = 0; event < events; event++){
				if(studentAttendsEventMatrix[student][event]){
					if(timeForEvent[event] != -1){
						countLectures[timeForEvent[event]]++;
					}
				}
			}
			for(int t = 0; t < spots; ++t){
				if(countLectures[t] > 1){
					numberStudentsHaveMoreLecturesAtSameTime += countLectures[t] - 1;
				}
			}
			for(int d = 0; d < days; ++d){
				int lecturesAtThisDay = 0;
				for(int h = 0; h < hours; h++){
					int t = d * hours + h;
					if(countLectures[t] > 0){
						lecturesAtThisDay++;
					}
				}
				if(lecturesAtThisDay == 1){
					onlyOneLectureADay++;
				}
				for(int h = 2; h < hours; h++){
					int t = d * hours + h;
					if(countLectures[t] > 0 && countLectures[t - 1] > 0 && countLectures[t - 2] > 0){
						longConsecutiveLectures++;
					}
				}
				if(countLectures[(d+1)*hours - 1] > 0){
					++lastTimeSlotADay;
				}
			}
		}
		/*result = lastTimeSlotADay 
				+ onlyOneLectureADay
				+ longConsecutiveLectures 
				+ numberStudentsHaveMoreLecturesAtSameTime * days * hours * students * 10
				+ unscheduledEvents * days * hours * students * students;
		System.out.println("last time slot a day: " + lastTimeSlotADay); 
		System.out.println("only one lecture a day: " + onlyOneLectureADay);
		System.out.println("long consecutive lectures: " + longConsecutiveLectures); 
		System.out.println("number students have more lectures at same time: " + numberStudentsHaveMoreLecturesAtSameTime);
		System.out.println("unscheduled Events: " + unscheduledEvents);*/
		return new EAResult(instance, new TimetablingValue(lastTimeSlotADay, onlyOneLectureADay, longConsecutiveLectures, numberStudentsHaveMoreLecturesAtSameTime, unscheduledEvents, eventTakesPlaceInRoomWithoutNeededFeatures));
	}

}
